package com.zx.video28181.sip;


import android.annotation.SuppressLint;
import android.gov.nist.javax.sip.SipStackImpl;
import android.gov.nist.javax.sip.header.From;
import android.gov.nist.javax.sip.header.To;
import android.gov.nist.javax.sip.message.MessageFactoryImpl;
import android.gov.nist.javax.sip.message.SIPRequest;
import android.gov.nist.javax.sip.stack.SIPServerTransaction;
import android.hardware.Camera;
import android.javax.sip.DialogTerminatedEvent;
import android.javax.sip.IOExceptionEvent;
import android.javax.sip.InvalidArgumentException;
import android.javax.sip.PeerUnavailableException;
import android.javax.sip.RequestEvent;
import android.javax.sip.ResponseEvent;
import android.javax.sip.ServerTransaction;
import android.javax.sip.SipException;
import android.javax.sip.SipFactory;
import android.javax.sip.SipListener;
import android.javax.sip.SipProvider;
import android.javax.sip.TimeoutEvent;
import android.javax.sip.TransactionAlreadyExistsException;
import android.javax.sip.TransactionTerminatedEvent;
import android.javax.sip.TransactionUnavailableException;
import android.javax.sip.address.Address;
import android.javax.sip.address.AddressFactory;
import android.javax.sip.address.SipURI;
import android.javax.sip.header.AuthorizationHeader;
import android.javax.sip.header.CSeqHeader;
import android.javax.sip.header.CallIdHeader;
import android.javax.sip.header.ContactHeader;
import android.javax.sip.header.ContentTypeHeader;
import android.javax.sip.header.ExpiresHeader;
import android.javax.sip.header.FromHeader;
import android.javax.sip.header.HeaderFactory;
import android.javax.sip.header.MaxForwardsHeader;
import android.javax.sip.header.ToHeader;
import android.javax.sip.header.UserAgentHeader;
import android.javax.sip.header.ViaHeader;
import android.javax.sip.header.WWWAuthenticateHeader;
import android.javax.sip.message.Message;
import android.javax.sip.message.MessageFactory;
import android.javax.sip.message.Request;
import android.javax.sip.message.Response;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import androidx.annotation.RequiresApi;

import com.zx.video28181.activity.AudioRecordIm;
import com.zx.video28181.activity.GB28181Params;
import com.zx.video28181.activity.MainActivity;
import com.zx.video28181.delayed_task.DelayQueueManager;
import com.zx.video28181.delayed_task.DelayTask;
import com.zx.video28181.delayed_task.Prefix;
import com.zx.video28181.domain.Device;
import com.zx.video28181.domain.SipMessageType;
import com.zx.video28181.util.MD5Util;
import com.zx.video28181.util.PSmuxer;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingDeque;

public class ClientListener implements SipListener {
    private static final String TAG = "ClientListener";

    private AddressFactory addressFactory;

    private HeaderFactory headerFactory;

    private MessageFactory messageFactory;

    private SipProvider sipProvider;

    private DelayQueueManager manager = new DelayQueueManager();

    private InitAboutCamera initAboutCamera;
    private GetAudioRecord getAudioRecord;
    /**
     * 心跳未响应次数
     */
    public static Map<String, Integer> keepalive = new ConcurrentHashMap<>();
    /**
     * 判断是否是心跳会议的响应
     */
    public static Map<String, String> keepaliveCallId = new ConcurrentHashMap<>();

    private Handler handler;

    private int ssrc = 0;
    private long time = 0;
    private int cseq = 0;
    private MediaCodec mediaCodec;
    public byte[] configByte;
    private DatagramSocket mediaSocket;
    private long count = 0;
    private byte[] mPreviewBuffer;
    private BlockingDeque<byte[]> mQueue = new LinkedBlockingDeque<>();

    public ClientListener(AddressFactory addressFactory, HeaderFactory headerFactory, MessageFactory messageFactory,
                          SipProvider sipProvider, byte[] mPreviewBuffer, InitAboutCamera initAboutCamera) {
        super();
        this.addressFactory = addressFactory;
        this.headerFactory = headerFactory;
        this.messageFactory = messageFactory;
        this.sipProvider = sipProvider;
        this.mPreviewBuffer = mPreviewBuffer;
        this.initAboutCamera = initAboutCamera;
        getAudioRecord = new AudioRecordIm();
    }


    /**
     * 推流后重新初始化相机相关信息
     */
    public interface InitAboutCamera {
        void initAboutCamera();
    }

    public void setinitAboutCamera(InitAboutCamera initAboutCamera) {
        this.initAboutCamera = initAboutCamera;
    }

    public void SetCameraCallback() {
        GB28181Params.getmCamera().addCallbackBuffer(mPreviewBuffer);
        //设置监听获取视频流的每一帧
        GB28181Params.getmCamera().setPreviewCallbackWithBuffer(mPreviewCallback);
        GB28181Params.getmCamera().startPreview();
        try {
            mediaSocket = new DatagramSocket(0);
        } catch (Exception e) {
            Log.e(e.getMessage(), e.toString());
            e.printStackTrace();
        }
    }

    public Camera.PreviewCallback mPreviewCallback = new Camera.PreviewCallback() {
        @Override
        public void onPreviewFrame(byte[] data, Camera camera) {
            if (data == null) {
                GB28181Params.getmCamera().addCallbackBuffer(mPreviewBuffer);
            } else {
                GB28181Params.getmCamera().addCallbackBuffer(data);
                handler.obtainMessage(0, mPreviewBuffer).sendToTarget();
            }
        }
    };

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public void processRequest(RequestEvent event) {
        System.out.println("processRequest执行");
        Request request = event.getRequest();
        Log.i("内容: {}", request.toString());
        CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
        String callId = callIdHeader.getCallId();
        CSeqHeader cseqHeader = (CSeqHeader) request.getHeader(CSeqHeader.NAME);
        String method = cseqHeader.getMethod();
        SendMediaThread thread = null;
        if (method.equalsIgnoreCase(SipMessageType.ACK)) {
            thread = new SendMediaThread();
            thread.start();
            getAudioRecord.init();
            AudioRecordThread audioRecordThread = new AudioRecordThread();
            audioRecordThread.start();
            //初始化Camera、编码器
//                    InitMediaCodec();


            initAboutCamera.initAboutCamera();
            SetCameraCallback();
            initMediaCodec();
//                    initCamera();
            //启动编码器
            mediaCodec.start();
            Log.i("Mediacodec", "mediaCodec.start()");
            //Camera 回调、启动Camera预览
//                    SetCameraCallback();

            GB28181Params.setCurDevicePlayMediaState(1);
            GB28181Params.setCameraState(GB28181Params.getCameraState() + 1);

        } else if (method.equalsIgnoreCase(SipMessageType.INVITE)) {
            try {
                String fromUri = event.getRequest().getHeader(To.NAME).toString();
                String deviceId = fromUri.substring(fromUri.indexOf(":") + 1, fromUri.indexOf("@")).split(":")[1];
                Log.i("设备id: {}", deviceId);
                Device d = MainActivity.ds.get(deviceId);
                if (d == null) {
                    Log.e("设备对象为空", "error");
                    return;
                }

                // 获取SDP
                Properties p = getProperties(request.getRawContent());
                String ssrc = p.getProperty("y");
                String ip = p.getProperty("c").split(" ")[2];
                String port = p.getProperty("m").split(" ")[1];
                GB28181Params.setMediaServerIPAddress(ip);
                GB28181Params.setMediaServerPort(Integer.parseInt(port));
                boolean isTcp = p.getProperty("m").contains("TCP");

                StringBuffer content = new StringBuffer(200);
                content.append("v=0\r\n");
                content.append("o=" + deviceId + " 0 0 IN IP4 " + ip + "\r\n");
                content.append("s=" + "Play" + "\r\n");
                content.append("c=IN IP4 " + ip + "\r\n");
                content.append("t=0 0\r\n");
                if (isTcp) {
                    content.append("m=video " + port + " TCP/RTP/AVP 96\r\n");
                } else {
                    content.append("m=video " + port + " RTP/AVP 96\r\n");
                }
                content.append("a=sendonly\r\n");
                content.append("a=rtpmap:96 PS/90000\r\n");
                content.append("y=" + ssrc + "\r\n");
                responseAck(event, content.toString());
            } catch (Exception e) {
                Log.i("sdp解析错误", "error");
                e.printStackTrace();
            }
        } else if (method.equalsIgnoreCase(SipMessageType.BYE)) {
            response(event, null, 200, null);
            thread.stop();
        }
    }

    public void ReleaseCamera() {
        if (GB28181Params.getmCamera() != null && GB28181Params.getCameraState() <= 0) {
            Log.i("ReleaseCamera", "" + GB28181Params.getCameraState() + "释放Camera资源");
            GB28181Params.getmCamera().stopPreview();
            GB28181Params.getmCamera().setPreviewCallbackWithBuffer(null);
//            GB28181Params.getmCamera().release();
//            GB28181Params.setmCamera(null);
        }
    }

    public void response(RequestEvent evt, Response resp, Integer statusCode, String msg) {
        try {
            if (resp == null && statusCode == null) throw new RuntimeException("参数不合法");
            // 为空则创建 | 使用传进来的
            if (resp == null) resp = messageFactory.createResponse(statusCode, evt.getRequest());
            ServerTransaction serverTransaction = getServerTransaction(evt);
            serverTransaction.sendResponse(resp);
            int code = resp.getStatusCode();
            if (code >= 200 && !"NOTIFY".equals(evt.getRequest().getMethod())) {
                if (serverTransaction.getDialog() != null) {
                    serverTransaction.getDialog().delete();
                }
            }
        } catch (ParseException | SipException | InvalidArgumentException e) {
            Log.e("发送响应错误{}", e.getMessage());
            e.printStackTrace();
        }
    }

    public void responseAck(RequestEvent evt, String sdp) throws SipException, InvalidArgumentException, ParseException {
        Response response = messageFactory.createResponse(Response.OK, evt.getRequest());
        SipFactory sipFactory = SipFactory.getInstance();
        ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
        response.setContent(sdp, contentTypeHeader);

        SipURI sipURI = (SipURI) evt.getRequest().getRequestURI();

        Address concatAddress = sipFactory.createAddressFactory().createAddress(
                sipFactory.createAddressFactory().createSipURI(sipURI.getUser(), sipURI.getHost() + ":" + sipURI.getPort()
                ));
        response.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
        Log.i("回复ack消息：/n{}", response.toString());
        getServerTransaction(evt).sendResponse(response);
    }

    public ServerTransaction getServerTransaction(RequestEvent evt) {
        Request request = evt.getRequest();
        ServerTransaction serverTransaction = evt.getServerTransaction();

        if (serverTransaction == null) {
            try {
                if (isTcp(request)) {
                    SipStackImpl stack = (SipStackImpl) sipProvider.getSipStack();
                    serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest) request, true);
                    if (serverTransaction == null) {
                        serverTransaction = sipProvider.getNewServerTransaction(request);
                    }
                } else {
                    SipStackImpl stack = (SipStackImpl) sipProvider.getSipStack();
                    serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest) request, true);
                    if (serverTransaction == null) {
                        serverTransaction = sipProvider.getNewServerTransaction(request);
                    }
                }
            } catch (TransactionAlreadyExistsException | TransactionUnavailableException e) {
                Log.e("\n处理错误: {}", e.toString());
                e.printStackTrace();
            }
        }
        return serverTransaction;
    }

    public Properties getProperties(byte[] content) {
        Properties p = new Properties();
        try {
            p.load(new ByteArrayInputStream(content));
            return p;
        } catch (IOException e) {
            e.printStackTrace();
            Log.e("\n处理错误: {}", e.toString());
            return null;
        }
    }

    /**
     * 判断是Tcp还是 UDP
     *
     * @param request
     * @return boolean
     */
    public boolean isTcp(Message request) {
        boolean isTcp = false;
        ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
        String transport = reqViaHeader.getTransport();
        if (transport.equalsIgnoreCase("TCP")) {
            isTcp = true;
        }
        return isTcp;
    }

    @Override
    public void processResponse(ResponseEvent responseEvent) {
        System.out.println("processResponse执行");
        Response response = responseEvent.getResponse();
        if (null == response) {
            System.out.println("response is null.");
            return;
        }
        int statusCode = response.getStatusCode();
        System.out.println("返回码:" + response.getStatusCode());
        System.out.println("Response is :" + response);
        //获取设备id
        String uri = response.getHeader(From.NAME).toString();
        String deviceId = uri.substring(uri.indexOf(":") + 1, uri.indexOf("@")).split(":")[1];
        System.out.println("设备id:" + deviceId);

        CSeqHeader cseqHeader = (CSeqHeader) responseEvent.getResponse().getHeader(CSeqHeader.NAME);
        String method = cseqHeader.getMethod();
        System.out.println(method + "112321321");
        if (statusCode == Response.UNAUTHORIZED) {
            sendMessageAgain(response, deviceId);
        } else if (statusCode == Response.OK) {
            Device d = MainActivity.ds.get(deviceId);
            if (d == null) {
                ReleaseCamera();
                Log.d("", "设备对象为空");
                return;
            }
            CallIdHeader callIdHeader = (CallIdHeader) response.getHeader(CallIdHeader.NAME);
            String callId = callIdHeader.getCallId();
            d.setIsOnline("1");
            d.setRegister(true);
            MainActivity.ds.put(d.getDeviceId(), d);
            // 删除次数累计
            keepalive.remove(deviceId);
            // 删除id
            keepaliveCallId.remove(callId);
            // 定时发送心跳
            manager.put(new DelayTask(Prefix.keepalive, d.getDeviceId(), 10000, () -> {
                sendKeepalive(d);
                Log.i("发送心跳完成 {} {}", d.getDeviceId() + d.getDeviceName());

            }));
            Log.i("", "设备向平台:{} 注册成功!");

        } else {
            Log.e("processResponse 失败: ", "重新发起注册");

        }

    }

    private Request createKeetpaliveMessageRequest(Device device, String context, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
        Request request = null;
        device.setDeviceIp(GB28181Params.getLocalSIPIPAddress());
        device.setDevicePort(GB28181Params.getLocalSIPPort());
        device.setDeviceId(GB28181Params.getLocalSIPDeviceId());
        device.setRegisterProtocol(GB28181Params.defaultProtol);
        String sipServerAddr = GB28181Params.getSIPServerIPAddress() + ":" + GB28181Params.getRemoteSIPServerPort();
        String deviceAddr = device.getDeviceIp() + ":" + device.getDevicePort();

        // sipuri
        SipURI requestURI = addressFactory.createSipURI(GB28181Params.getRemoteSIPServerID(), sipServerAddr);
        // via
        ArrayList<ViaHeader> viaHeaders = new ArrayList<>();
        ViaHeader viaHeader = headerFactory.createViaHeader(device.getDeviceIp(), device.getDevicePort(),
                device.getRegisterProtocol(), viaTag);
        viaHeader.setRPort();
        viaHeaders.add(viaHeader);
        // from
        SipURI fromSipURI = addressFactory.createSipURI(device.getDeviceId(), deviceAddr);
        Address fromAddress = addressFactory.createAddress(fromSipURI);
        FromHeader fromHeader = headerFactory.createFromHeader(fromAddress, fromTag);
        // to
        SipURI toSipURI = addressFactory.createSipURI(GB28181Params.getRemoteSIPServerID(), sipServerAddr);
        Address toAddress = addressFactory.createAddress(toSipURI);
        ToHeader toHeader = headerFactory.createToHeader(toAddress, toTag);
        // Forwards
        MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);
        // ceq
        CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(1L, Request.MESSAGE);
        request = messageFactory.createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
                toHeader, viaHeaders, maxForwards);
        List<String> agentParam = new ArrayList<>();
        //agentParam.add("my-client");
        UserAgentHeader userAgentHeader = headerFactory.createUserAgentHeader(agentParam);
        request.addHeader(userAgentHeader);
        ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("Application", "MANSCDP+xml");
        request.setContent(context, contentTypeHeader);
        return request;
    }

    /**
     * 发送心跳包
     *
     * @param device
     * @return
     */
    private static String getKeepalive(Device device) {
        StringBuffer keepaliveXml = new StringBuffer(200);
        keepaliveXml.append("<?xml version=\"1.0\"?>\r\n");
        keepaliveXml.append("<Notify>\r\n");
        keepaliveXml.append("<CmdType>Keepalive</CmdType>\r\n");
        keepaliveXml.append("<SN>" + (int) ((Math.random() * 9 + 1) * 100000) + "</SN>\r\n");
        keepaliveXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
        keepaliveXml.append("<Status>OK</Status>\r\n");
        keepaliveXml.append("</Notify>\r\n");
        return keepaliveXml.toString();
    }

    private void sendKeepalive(Device d) {
        try {
            // 心跳内容
            String context = getKeepalive(d);
            CallIdHeader callIdHeader = sipProvider.getNewCallId();
            String tm1 = UUID.randomUUID().toString();
            String tm2 = UUID.randomUUID().toString();
            Request request = createKeetpaliveMessageRequest(d, context, "z9hG4bK-Keepalive-" + tm1, tm2, null, callIdHeader);
            sipProvider.sendRequest(request);
            Log.i("发送心跳: \n{}", request.toString());
            // 存储心跳请求
            String callId = callIdHeader.getCallId();

            // 不为空就行
            keepaliveCallId.put(callId, "xxx");

            // 没收到响应移除 callId
            manager.put(new DelayTask(5000L, () -> {
                String cId = keepaliveCallId.get(callId);
                if (null != cId) {
                    // 移除
                    keepaliveCallId.remove(callId);
                    // 同时增加一次
                    Integer sum = keepalive.get(d.getDeviceId());
                    keepalive.put(d.getDeviceId(), sum != null ? ++sum : 1);
                    // 超过3次设置为离线
                    if (d.isRegister() && sum != null && sum >= 3) {
                        Device device = MainActivity.ds.get(d.getDeviceId());
                        device.setRegister(false);
                        MainActivity.ds.put(d.getDeviceId(), device);
                    }

                }
            }));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Request createMessageRequest(Device d, String content, String fromTag, String viaBranch, CallIdHeader callIdHeader) throws PeerUnavailableException, ParseException, InvalidArgumentException {
        Request request = null;
        d.setDeviceIp("192.168.10.11");
        d.setDevicePort(5099);
        d.setDeviceId(GB28181Params.getLocalSIPDeviceId());
        d.setRegisterProtocol("UDP");
        String sipAddress = GB28181Params.getSIPServerIPAddress() + ":" + GB28181Params.getRemoteSIPServerPort();
        String deviceAddress = d.getDeviceIp() + ":" + d.getDevicePort();
        //请求行
        // sipuri
        SipURI requestURI = addressFactory.createSipURI(GB28181Params.getRemoteSIPServerID(), sipAddress);
        // via
        ArrayList<ViaHeader> viaHeaders = new ArrayList<>();
        ViaHeader viaHeader = headerFactory.createViaHeader(d.getDeviceIp(), d.getDevicePort(), d.getRegisterProtocol(), viaBranch);
        viaHeader.setRPort();
        viaHeaders.add(viaHeader);
        // from
        SipURI fromSipURI = addressFactory.createSipURI(d.getDeviceId(), deviceAddress);
        Address fromAddress = addressFactory.createAddress(fromSipURI);
        FromHeader fromHeader = headerFactory.createFromHeader(fromAddress, fromTag);
        // to
        SipURI toSipURI = addressFactory.createSipURI(GB28181Params.getRemoteSIPServerID(), sipAddress);
        Address toAddress = addressFactory.createAddress(toSipURI);
        ToHeader toHeader = headerFactory.createToHeader(toAddress, null);
        // Forwards
        MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);
        // ceq
        CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(1L, Request.MESSAGE);
        MessageFactoryImpl messageFactoryImpl = (MessageFactoryImpl) messageFactory;
        // 设置编码， 防止中文乱码
        messageFactoryImpl.setDefaultContentEncodingCharset(d.getCharset());
        request = messageFactory.createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
                toHeader, viaHeaders, maxForwards);
        List<String> agentParam = new ArrayList<>();
        agentParam.add("VM-gb-client");
        UserAgentHeader userAgentHeader = headerFactory.createUserAgentHeader(agentParam);
        request.addHeader(userAgentHeader);
        ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("APPLICATION", "MANSCDP+xml");
        request.setContent(content, contentTypeHeader);
        return request;
    }

    private void sendMessageAgain(Response response, String deviceId) {
        CallIdHeader callIdHeader = (CallIdHeader) response.getHeader(CallIdHeader.NAME);
        String callId = callIdHeader.getCallId();

        int statusCode = response.getStatusCode();
        if (statusCode == 401) {
            try {
                WWWAuthenticateHeader www = (WWWAuthenticateHeader) response.getHeader(WWWAuthenticateHeader.NAME);
                System.out.println("向平台:发送带认证信息的注册消息!");
                // 发送注册
                CallIdHeader callIdHeader1 = sipProvider.getNewCallId();
                callIdHeader1.setCallId(callId);
                int exs = 3600;
                Device device = MainActivity.ds.get(deviceId);
                if (device == null) {
                    exs = 0;
                }
                Request registerRequest = sendMessage("", "", "", exs);
                String realm = www.getRealm();
                String nonce = www.getNonce();
                String scheme = www.getScheme();
                // 参考 https://blog.csdn.net/y673533511/article/details/88388138
                // qop 保护质量 包含auth（默认的）和auth-int（增加了报文完整性检测）两种策略
                String qop = www.getQop();
                SipURI requestURI = addressFactory.createSipURI(GB28181Params.getLocalSIPDeviceId(), GB28181Params.getLocalSIPIPAddress() + ":" + GB28181Params.getLocalSIPPort());
                String cNonce = null;
                String nc = "00000001";
                if (qop != null) {
                    if ("auth".equals(qop)) {
                        // 客户端随机数，这是一个不透明的字符串值，由客户端提供，并且客户端和服务器都会使用，以避免用明文文本。
                        // 这使得双方都可以查验对方的身份，并对消息的完整性提供一些保护
                        cNonce = UUID.randomUUID().toString();
                    } else if ("auth-int".equals(qop)) {
                        // TODO
                    }
                }
                String HA1 = MD5Util.MD5(GB28181Params.getLocalSIPDeviceId() + ":" + realm + ":" + "12345678");
                String HA2 = MD5Util.MD5(Request.REGISTER + ":" + requestURI.toString());
                StringBuffer reStr = new StringBuffer(200);
                reStr.append(HA1);
                reStr.append(":");
                reStr.append(nonce);
                reStr.append(":");
                if (qop != null) {
                    reStr.append(nc);
                    reStr.append(":");
                    reStr.append(cNonce);
                    reStr.append(":");
                    reStr.append(qop);
                    reStr.append(":");
                }
                reStr.append(HA2);
                String RESPONSE = MD5Util.MD5(reStr.toString());
                AuthorizationHeader authorizationHeader = headerFactory.createAuthorizationHeader(scheme);
                authorizationHeader.setUsername(GB28181Params.getLocalSIPDeviceId());
                authorizationHeader.setRealm(realm);
                authorizationHeader.setNonce(nonce);
                authorizationHeader.setURI(requestURI);
                authorizationHeader.setResponse(RESPONSE);
                authorizationHeader.setAlgorithm("MD5");
                if (qop != null) {
                    authorizationHeader.setQop(qop);
                    authorizationHeader.setCNonce(cNonce);
                    authorizationHeader.setNonceCount(1);
                }
                registerRequest.addHeader(authorizationHeader);
                sipProvider.sendRequest(registerRequest);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }

    }

    private Request sendMessage(String s, String s1, String s2, int exs) throws Exception {
        SipURI from = addressFactory.createSipURI(GB28181Params.getLocalSIPDeviceId(), GB28181Params.getLocalSIPIPAddress() + ":" + GB28181Params.getLocalSIPPort());
        Address fromNameAddress = addressFactory.createAddress(from);
        FromHeader fromHeader = headerFactory.createFromHeader(fromNameAddress, "From-Register-");

        //to
        SipURI toAddress = addressFactory.createSipURI(GB28181Params.getLocalSIPDeviceId(), GB28181Params.getLocalSIPIPAddress() + ":" + GB28181Params.getLocalSIPPort());
        Address toNameAddress = addressFactory.createAddress(toAddress);
        ToHeader toHeader = headerFactory.createToHeader(toNameAddress, null);

        SipURI requestURI = addressFactory.createSipURI(GB28181Params.getRemoteSIPServerID(), GB28181Params.getSIPServerIPAddress() + ":" + GB28181Params.getRemoteSIPServerPort());
        requestURI.setTransportParam(GB28181Params.defaultProtol);
        //via
        ArrayList<ViaHeader> viaHeaders = new ArrayList<>();
        ViaHeader viaHeader = headerFactory.createViaHeader(GB28181Params.getSIPServerIPAddress(), GB28181Params.getRemoteSIPServerPort(), GB28181Params.defaultProtol, "branch1");
        viaHeader.setRPort();
        viaHeaders.add(viaHeader);

        CallIdHeader callIdHeader = sipProvider.getNewCallId();
        @SuppressWarnings("deprecation")
        CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(1, Request.REGISTER);
        MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);

        Request request = messageFactory.createRequest(requestURI, Request.REGISTER, callIdHeader, cSeqHeader,
                fromHeader, toHeader, viaHeaders, maxForwards);
        SipURI contactURI = addressFactory.createSipURI(GB28181Params.getLocalSIPDeviceId(), GB28181Params.getLocalSIPIPAddress() + ":" + GB28181Params.getLocalSIPPort());
        Address contactAddress = addressFactory.createAddress(contactURI);
        ContactHeader contactHeader = headerFactory.createContactHeader(contactAddress);
        ExpiresHeader expiresHeader = headerFactory.createExpiresHeader(exs);
        request.addHeader(expiresHeader);
        request.addHeader(contactHeader);
        return request;
    }

    @Override
    public void processTimeout(TimeoutEvent timeoutEvent) {
    }

    @Override
    public void processIOException(IOExceptionEvent exceptionEvent) {
    }

    @Override
    public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
        System.out.println("processTransactionTerminated执行");
    }

    @Override
    public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) {
        System.out.println("processDialogTerminated执行");
    }

    private void initMediaCodec() {
        try {
            Log.d("initMediaCodec: ", "初始化编码器");
            mediaCodec = MediaCodec.createEncoderByType("video/avc");
            //height和width一般都是照相机的height和width。
            //TODO 因为获取到的视频帧数据是逆时针旋转了90度的，所以这里宽高需要对调
            MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", GB28181Params.getCameraWidth(), GB28181Params.getCameraHeigth());
            //描述平均位速率（以位/秒为单位）的键。 关联的值是一个整数
            mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, GB28181Params.getCameraWidth() * GB28181Params.getCameraHeigth());
            //描述视频格式的帧速率（以帧/秒为单位）的键。帧率，一般在15至30之内，太小容易造成视频卡顿。
            mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 25);
            //色彩格式，具体查看相关API，不同设备支持的色彩格式不尽相同
            mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
            //关键帧间隔时间，单位是秒
            mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
            mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            //开始编码
//            mediaCodec.start();
            Log.d("initMediaCodec: ", "初始化编码器结束");
        } catch (Exception e) {
            Log.e(e.getMessage(), e.toString());
            e.printStackTrace();
        }
    }

    private void NV21ToNV12(byte[] nv21, byte[] nv12, int width, int height) {
        if (nv21 == null || nv12 == null) return;
        int framesize = width * height;
        int i = 0, j = 0;
        System.arraycopy(nv21, 0, nv12, 0, framesize);
        for (i = 0; i < framesize; i++) {
            nv12[i] = nv21[i];
        }
        for (j = 0; j < framesize / 2; j += 2) {
            nv12[framesize + j - 1] = nv21[j + framesize];
        }
        for (j = 0; j < framesize / 2; j += 2) {
            nv12[framesize + j] = nv21[j + framesize - 1];
        }
    }

    /**
     * 此处为顺时针旋转旋转90度
     *
     * @param data        旋转前的数据
     * @param imageWidth  旋转前数据的宽
     * @param imageHeight 旋转前数据的高
     * @return 旋转后的数据
     */
    private byte[] rotateNV290(byte[] data, int imageWidth, int imageHeight) {
        byte[] yuv = new byte[imageWidth * imageHeight * 3 / 2];
        // Rotate the Y luma
        int i = 0;
        for (int x = 0; x < imageWidth; x++) {
            for (int y = imageHeight - 1; y >= 0; y--) {
                yuv[i] = data[y * imageWidth + x];
                i++;
            }
        }
        // Rotate the U and V color components
        i = imageWidth * imageHeight * 3 / 2 - 1;
        for (int x = imageWidth - 1; x > 0; x = x - 2) {
            for (int y = 0; y < imageHeight / 2; y++) {
                yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + x];
                i--;
                yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + (x - 1)];
                i--;
            }
        }
        return yuv;
    }

    private byte[] EncodeH264(byte[] input) {
        try {
            byte[] yuv420sp = new byte[GB28181Params.getCameraWidth() * GB28181Params.getCameraHeigth() * 3 / 2];
            NV21ToNV12(input, yuv420sp, GB28181Params.getCameraWidth(), GB28181Params.getCameraHeigth());
            input = yuv420sp;
            int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
            ByteBuffer inputBuffer = mediaCodec.getInputBuffer(inputBufferIndex);
            if (inputBuffer == null) {
                return null;
            }
            inputBuffer.clear();
            inputBuffer.put(input);

            mediaCodec.queueInputBuffer(inputBufferIndex, 0, input.length, 132 + 1000000 * count / 25, 0);
            count++;
            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
            int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
            while (outputBufferIndex >= 0) {
                ByteBuffer outputBuffer = mediaCodec.getOutputBuffer(outputBufferIndex);
                if (outputBuffer == null) {
                    return null;
                }
                byte[] outData = new byte[outputBuffer.remaining()];
                outputBuffer.get(outData, 0, outData.length);
                if (bufferInfo.flags == MediaCodec.BUFFER_FLAG_CODEC_CONFIG) {
                    configByte = new byte[bufferInfo.size];
                    configByte = outData;
                }
                if (bufferInfo.flags == MediaCodec.BUFFER_FLAG_KEY_FRAME) {
                    byte[] keyframe = new byte[bufferInfo.size + configByte.length];
                    System.arraycopy(configByte, 0, keyframe, 0, configByte.length);
                    System.arraycopy(outData, 0, keyframe, configByte.length, outData.length);
                    mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                    return keyframe;
                }
                mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                return outData;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    class SendMediaThread extends Thread {
        @SuppressLint("HandlerLeak")
        @Override
        public void run() {
            Looper.prepare();
            handler = new Handler() {
                public void handleMessage(android.os.Message msg) {
                    if (msg.what == 0) {
                        byte[] raw = EncodeH264(mPreviewBuffer);
                        if (raw != null) {
                            int index = 0;
                            int length;
                            List<byte[]> list = new ArrayList<>();
                            byte[] naul;
                            //获取一帧H264所有的naul
                            for (int i = index + 3; i < raw.length - 3; i++) {
                                if ((raw[i] == 0x00 && raw[i + 1] == 0x00 && raw[i + 2] == 0x00 && raw[i + 3] == 0x01)) {
                                    length = i - index - 4;
                                    naul = new byte[length];
                                    System.arraycopy(raw, index + 4, naul, 0, length);
                                    list.add(naul);
                                    index += length + 4;
                                }
                                if (i == raw.length - 3 - 1) {
                                    naul = new byte[raw.length - index - 4];
                                    System.arraycopy(raw, index + 4, naul, 0, naul.length);
                                    list.add(naul);
                                }
                            }
                            Log.e(TAG, "handleMessage: " + list.size() + "");
                            byte[] psH;
                            if (list.size() == 3) {
                                psH = PSmuxer.GetPSHeader(0, time, 1);
                            } else {
                                psH = PSmuxer.GetPSHeader(0, time, 0);
                            }
                            byte[] Temp = new byte[psH.length + 14 * list.size() + raw.length];
                            byte[] startcode = new byte[]{0x00, 0x00, 0x00, 0x01};
                            long pts = 132 + 1000000 * cseq / 25;
                            switch (list.size()) {
                                case 1:
                                    byte[] pesh = PSmuxer.GetPESHeader(list.get(0).length + startcode.length, 0, pts);
                                    System.arraycopy(psH, 0, Temp, 0, psH.length);
                                    System.arraycopy(pesh, 0, Temp, psH.length, pesh.length);
                                    System.arraycopy(startcode, 0, Temp, psH.length + pesh.length, startcode.length);
                                    System.arraycopy(list.get(0), 0, Temp, psH.length + pesh.length + 4, list.get(0).length);
                                    break;
                                case 2:
                                    byte[] pesh_SPS = PSmuxer.GetPESHeader(list.get(0).length + startcode.length, 0, pts);
                                    byte[] pesh_PPS = PSmuxer.GetPESHeader(list.get(1).length + startcode.length, 0, pts);
                                    System.arraycopy(psH, 0, Temp, 0, psH.length);
                                    System.arraycopy(pesh_SPS, 0, Temp, psH.length, pesh_SPS.length);
                                    System.arraycopy(startcode, 0, Temp, psH.length + pesh_SPS.length, startcode.length);
                                    System.arraycopy(list.get(0), 0, Temp, psH.length + pesh_SPS.length + 4, list.get(0).length);
                                    System.arraycopy(pesh_PPS, 0, Temp, psH.length + pesh_SPS.length + list.get(0).length + 4, pesh_PPS.length);
                                    System.arraycopy(startcode, 0, Temp, psH.length + pesh_SPS.length + list.get(0).length + 4 + pesh_PPS.length, startcode.length);
                                    System.arraycopy(list.get(1), 0, Temp, psH.length + pesh_SPS.length + 4 + list.get(0).length + pesh_PPS.length + 4, list.get(1).length);
                                    break;
                                case 3:
                                    byte[] pesH_SPS = PSmuxer.GetPESHeader(list.get(0).length + startcode.length, 0, pts);
                                    byte[] pesH_PPS = PSmuxer.GetPESHeader(list.get(1).length + startcode.length, 0, pts);
                                    byte[] pesH_RAW = PSmuxer.GetPESHeader(list.get(2).length + startcode.length, 0, pts);
                                    System.arraycopy(psH, 0, Temp, 0, psH.length);
                                    System.arraycopy(pesH_SPS, 0, Temp, psH.length, pesH_SPS.length);
                                    System.arraycopy(startcode, 0, Temp, psH.length + pesH_SPS.length, startcode.length);
                                    System.arraycopy(list.get(0), 0, Temp, psH.length + pesH_SPS.length + 4, list.get(0).length);
                                    System.arraycopy(pesH_PPS, 0, Temp, psH.length + pesH_SPS.length + 4 + list.get(0).length, pesH_PPS.length);
                                    System.arraycopy(startcode, 0, Temp, psH.length + pesH_SPS.length + 4 + list.get(0).length + pesH_PPS.length, startcode.length);
                                    System.arraycopy(list.get(1), 0, Temp, psH.length + pesH_SPS.length + 4 + list.get(0).length + pesH_PPS.length + 4, list.get(1).length);
                                    System.arraycopy(pesH_RAW, 0, Temp, psH.length + pesH_SPS.length + 4 + list.get(0).length + pesH_PPS.length + 4 + list.get(1).length, pesH_RAW.length);
                                    System.arraycopy(startcode, 0, Temp, psH.length + pesH_SPS.length + 4 + list.get(0).length + pesH_PPS.length + 4 + list.get(1).length + pesH_RAW.length, startcode.length);
                                    System.arraycopy(list.get(2), 0, Temp, psH.length + pesH_SPS.length + 4 + list.get(0).length + pesH_PPS.length + 4 + list.get(1).length + pesH_RAW.length + 4, list.get(2).length);
                                    break;
                            }

                            List<byte[]> rtplist = SplitByteArray(Temp, 1400);
                            byte[] buf;
                            InetSocketAddress address = new InetSocketAddress(GB28181Params.getMediaServerIPAddress(), GB28181Params.getMediaServerPort());
                            // audio
                            byte[] audioBuf;
                            byte[] audioData = getAudioRecord.getdata();
                            if (audioData != null) {
                                byte[] pesHeader = PSmuxer.GetPESHeader(audioData.length, 1, pts);
                                byte[] rtpH = PSmuxer.GetRtpHeader(0, cseq, time, ssrc);
                                audioBuf = new byte[audioData.length + rtpH.length + pesHeader.length];
                                DatagramPacket packet = new DatagramPacket(audioBuf, audioBuf.length, address);
                                try {
                                    mediaSocket.send(packet);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }

                            }
                            for (int i = 0; i < rtplist.size(); i++) {
                                byte[] rtpH = PSmuxer.GetRtpHeader(0, cseq, time, ssrc);
                                byte[] rtpHend = PSmuxer.GetRtpHeader(1, cseq, time, ssrc);
                                if (i != rtplist.size() - 1) {
                                    buf = new byte[rtplist.get(i).length + 12];
                                    System.arraycopy(rtpH, 0, buf, 0, rtpH.length);
                                    System.arraycopy(rtplist.get(i), 0, buf, 12, rtplist.get(i).length);
                                } else {
                                    buf = new byte[rtplist.get(i).length + 12];
                                    System.arraycopy(rtpHend, 0, buf, 0, rtpHend.length);
                                    System.arraycopy(rtplist.get(i), 0, buf, rtpHend.length, rtplist.get(i).length);
                                }
                                DatagramPacket packet = new DatagramPacket(buf, buf.length, address);
                                try {
                                    mediaSocket.send(packet);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                                cseq += 1;
                            }
                            time += 3600;
                        }
                    }
                }
            };
            Looper.loop();

        }
    }

    private List<byte[]> SplitByteArray(byte[] input, int size) {
        List<byte[]> result = new ArrayList<>();
        int length = input.length;
        int count = length / size;
        int r = length % size;

        for (int i = 0; i < count; i++) {
            byte[] newByteArray = new byte[size];
            System.arraycopy(input, size * i, newByteArray, 0, size);
            result.add(newByteArray);
        }
        if (r != 0) {
            byte[] newByteArray = new byte[r];
            System.arraycopy(input, length - r, newByteArray, 0, r);
            result.add(newByteArray);
        }
        return result;
    }





    public interface GetAudioRecord{
        void init();
        byte[] startRecord();
        byte[] getdata();

    }


     class AudioRecordThread extends Thread{

        public void run(){
            getAudioRecord.startRecord();
        }
    }
}
